package com.slipper.system.codeless.service;

import java.util.*;
import java.util.stream.Collectors;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

import com.slipper.constants.Constants;
import com.slipper.core.database.IResolver;
import com.slipper.core.database.ResolverFactory;
import com.slipper.datasource.DatasourceUtil;
import com.slipper.system.codeless.bo.DatabaseBO;
import com.slipper.system.codeless.dao.DatabaseDao;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;

import com.slipper.system.codeless.dao.DatasourceDao;
import com.slipper.system.codeless.bo.DatasourceBO;

import com.slipper.core.transaction.ReadTransactional;
import com.slipper.core.transaction.WriteTransactional;
import com.slipper.util.Tool;
import com.slipper.util.UUID;

@Service
@ReadTransactional
public class DatasourceService {
    @Resource
    private DatasourceDao datasourceDao;
    @Resource
    private DatabaseDao databaseDao;

    public DatasourceBO queryBy(String id) {
        return datasourceDao.queryBy(id);
    }

    public DatasourceBO queryByCode(String code) {
        return datasourceDao.queryByCode(code);
    }

    public List<DatasourceBO> queryList(DatasourceBO bo) {
        return datasourceDao.queryList(bo);
    }

    public PageInfo<DatasourceBO> queryPage(DatasourceBO bo) {
        PageHelper.startPage(bo.getPageNumber(), bo.getPageSize());
        return new PageInfo<>(datasourceDao.queryList(bo));
    }

    @WriteTransactional
    public int save(DatasourceBO bo) {
        if (Tool.isBlank(bo.getCode())) {
            do {
                bo.setCode(UUID.getUUID());
            } while (existsCode(bo.getId(), bo.getCode()));
        }

        DatasourceUtil.clean(bo.getCode());
        if (Tool.isNotBlank(bo.getId())) {
            bo.setUpdateTime(System.currentTimeMillis());
            return datasourceDao.update(bo);
        } else {
            bo.setId(UUID.getUUID());
            bo.setUpdateTime(System.currentTimeMillis());
            bo.setCreateTime(System.currentTimeMillis());
            return datasourceDao.insert(bo);
        }
    }

    public boolean existsCode(String id, String code) {
        DatasourceBO bo = new DatasourceBO();
        bo.setId(id);
        bo.setCode(code);
        return datasourceDao.existsCode(bo) > 0;
    }

    @WriteTransactional
    public int delete(DatasourceBO bo) {
        return datasourceDao.delete(bo);
    }

    public PageInfo<Map<String, Object>> queryTablePage(DatasourceBO bo) {
        DatasourceBO datasource = datasourceDao.queryBy(bo.getId());
        DatabaseBO database = databaseDao.queryBy(datasource.getDatabaseId());
        IResolver resolver = ResolverFactory.getResolver(database.getType());
        List<Map<String, Object>> tableList = DatasourceUtil.queryList(datasource, resolver.tableListSQL(null), null);
        tableList.forEach(table -> {
            table.put("datasourceId", bo.getId());
            table.put("tableName", table.remove("table_name"));
        });
        if (Tool.isNotBlank(bo.getTableName())) {
            tableList = tableList.stream()
                    .filter(table -> table.get("tableName").toString().contains(bo.getTableName()))
                    .collect(Collectors.toList());
        }

        int start = (bo.getPageNumber() - 1) * bo.getPageSize();
        int end = bo.getPageNumber() * bo.getPageSize();
        List<Map<String, Object>> resultList = Collections.emptyList();
        if (tableList.size() >= end) {
            resultList = tableList.subList(start, end);
        } else if (tableList.size() > start) {
            resultList = tableList.subList(start, tableList.size());
        }

        PageInfo<Map<String, Object>> pageInfo = new PageInfo<>();
        pageInfo.setList(resultList);
        pageInfo.setPageNum(bo.getPageNumber());
        pageInfo.setPageSize(bo.getPageSize());
        pageInfo.setTotal(tableList.size());
        return pageInfo;
    }

    public List<Map<String, Object>> queryTableList(DatasourceBO bo) {
        DatasourceBO datasource = datasourceDao.queryBy(bo.getId());
        DatabaseBO database = databaseDao.queryBy(datasource.getDatabaseId());
        IResolver resolver = ResolverFactory.getResolver(database.getType());
        List<Map<String, Object>> tableList = DatasourceUtil.queryList(datasource, resolver.tableListSQL(null), null);
        tableList.forEach(table -> {
            table.put("datasourceId", bo.getId());
            table.put("tableName", table.remove("table_name"));
        });
        return tableList;
    }

    public List<Map<String, Object>> queryTableColumnList(DatasourceBO bo) {
        DatasourceBO datasource = datasourceDao.queryBy(bo.getId());
        DatabaseBO database = databaseDao.queryBy(datasource.getDatabaseId());
        IResolver resolver = ResolverFactory.getResolver(database.getType());
        List<Map<String, Object>> columnList = DatasourceUtil.queryList(datasource, resolver.columnSQL(bo.getTableName()), null);
        columnList.forEach(column -> column.put("originName", column.get("name")));
        return columnList;
    }

    @SuppressWarnings("unchecked")
    public List<Map<String, Object>> queryColumnList(DatasourceBO bo) {
        DatasourceBO datasource = datasourceDao.queryBy(bo.getId());
        DatabaseBO database = databaseDao.queryBy(datasource.getDatabaseId());
        IResolver resolver = ResolverFactory.getResolver(database.getType());
        List<Map<String, Object>> columnList = DatasourceUtil.queryList(datasource, resolver.tableColumnListSQL("%"), null);
        Map<String, Map<String, Object>> tableMap = new HashMap<>();
        columnList.forEach(column -> {
            Map<String, Object> tableData = tableMap.get((String) column.get("table_name"));
            if (tableData == null) {
                tableData = new HashMap<>();
                tableData.put("tableName", column.get("table_name"));
                tableMap.put((String) column.get("table_name"), tableData);
            }
            if (Constants.YES.equals(column.get("column_pk"))) {
                tableData.put("pkIdName", column.get("column_name"));
            }

            List<Map<String, Object>> fieldList = (List<Map<String, Object>>) tableData.get("fieldList");
            if (fieldList == null) {
                fieldList = new ArrayList<>();
            }

            Map<String, Object> fieldMap = new HashMap<>();
            fieldMap.put("columnName", column.get("column_name"));
            fieldMap.put("columnType", column.get("column_type"));
            fieldMap.put("columnLength", column.get("column_length"));
            fieldMap.put("columnPrecision", column.get("column_precision"));
            fieldMap.put("columnScale", column.get("column_scale"));
            fieldList.add(fieldMap);

            tableData.put("fieldList", fieldList.stream()
                    .sorted(Comparator.comparing(field -> (String) field.get("columnName")))
                    .collect(Collectors.toList()));
        });
        return tableMap.values()
                .stream()
                .sorted(Comparator.comparing(table -> (String) table.get("tableName")))
                .collect(Collectors.toList());
    }
}
